package com.yuan.base.service.server.filter;

import com.yuan.base.service.api.BaseErrorCode;
import com.yuan.base.service.api.PlainResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Iterator;
import java.util.List;

import static com.yuan.base.service.infrastructure.constants.BaseCommonConstant.BAD_REQUEST_CODE;
import static com.yuan.base.service.infrastructure.constants.BaseCommonConstant.UNKNOWN_ERROR;

/**
 * @author lintiangang
 * @date 2021/03/26 10:23
 **/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Value("${spring.profiles.active:prod}")
    private String envTag;


    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public PlainResult handleException(MissingServletRequestParameterException e,
                                       HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE);
        return result;
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public PlainResult handleException(HttpMessageNotReadableException e, HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE);
        return result;
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    public PlainResult handleException(HttpMediaTypeNotSupportedException e, HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE.getCode(), "不支持的媒体类型");
        return result;
    }

    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    @ResponseBody
    public PlainResult handleException(HttpMediaTypeNotAcceptableException e, HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE.getCode(), "请求的媒体类型与服务端不匹配");
        return result;
    }

    @ExceptionHandler(TypeMismatchException.class)
    @ResponseBody
    public PlainResult handleException(TypeMismatchException e, HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE.getCode(), "参数类型不匹配");
        return result;
    }

    @ExceptionHandler(value = ArithmeticException.class)
    @ResponseBody
    public PlainResult arithmeticException(ArithmeticException e) {
        // 打印堆栈信息
        log.error(e.getMessage(), e);
        return PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE.getCode(), e.getMessage());
    }

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public PlainResult unknownException(Exception e, HttpServletResponse response) {
        log.error(e.getMessage(), e);

        PlainResult result = PlainResult.errorResult(BaseErrorCode.UNKNOWN_ERROR);
        response.setStatus(UNKNOWN_ERROR);
        return result;
    }


    /**
     * MethodArgumentNotValidException处理逻辑，自定义返回详细信息还是模糊信息
     * @param e
     * @param request
     * @param response
     * @return
     * @throws MethodArgumentNotValidException
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public PlainResult handleException(MethodArgumentNotValidException e, HttpServletRequest request, HttpServletResponse response) throws MethodArgumentNotValidException {
        BindingResult bindResult = e.getBindingResult();
        String message = null;
        if(bindResult != null) {
            message = getErrorMessage(bindResult.getAllErrors());
            log.info("参数异常：{}--{}", bindResult.getObjectName(), message);
        }
        PlainResult result;
        //测试环境或者内部运营接口返回实际错误信息
        if (!envTag.equals("prod")) {
            result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE.getCode(), message);
        } else {
            //未知参数异常
            result = PlainResult.errorResult(BaseErrorCode.BAD_REQUEST_CODE);
        }

        response.setStatus(BAD_REQUEST_CODE);
        return result;
    }


    private String getErrorMessage(List<ObjectError> errors) {
        StringBuilder errorMessage = new StringBuilder();
        Iterator message = errors.iterator();

        while(message.hasNext()) {
            ObjectError error = (ObjectError)message.next();
            if(error instanceof FieldError) {
                FieldError fieldError = (FieldError)error;
                errorMessage.append(fieldError.getField());
                errorMessage.append(":");
                errorMessage.append(fieldError.getDefaultMessage());
                errorMessage.append(",");
            }
        }

        String message1 = errorMessage.toString();
        if(errorMessage.length() > 0) {
            message1 = errorMessage.substring(0, errorMessage.length() - 1);
        }

        return message1;
    }
}
